package com.sangmo.fts.process.receivable.service;

import com.sangmo.boot.framework.common.errors.ServiceException;
import com.sangmo.boot.framework.common.errors.StandardErrors;
import com.sangmo.boot.framework.data.filter.NativeQueryBuilder;
import com.sangmo.boot.framework.data.filter.PageResult;
import com.sangmo.boot.framework.data.filter.PageSearch;
import com.sangmo.boot.framework.data.filter.ValueFilter;
import com.sangmo.boot.framework.plugin.dataview.crud.service.DelegateViewService;
import com.sangmo.boot.framework.plugin.dataview.crud.service.ViewService;
import com.sangmo.boot.framework.plugin.dataview.spi.jpa.NativeQueryBuilderFactory;
import com.sangmo.fts.basic.model.Company;
import com.sangmo.fts.common.authority.AuthorityFilterChain;
import com.sangmo.fts.common.authority.Rights;
import com.sangmo.fts.common.authority.sql.WhereModifier;
import com.sangmo.fts.process.receivable.interfaces.RpfWaybillReceiveRequest;
import com.sangmo.fts.process.receivable.model.RpfContract;
import com.sangmo.fts.process.receivable.model.RpfWaybill;
import com.sangmo.fts.process.receivable.model.RpfWaybillItem;
import com.sangmo.fts.process.storage.model.CsfWaybillItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Service
@Transactional
public class RpfWaybillService extends DelegateViewService<RpfWaybill> {

    @Autowired
    private RpfWaybillEntityService rpfWaybillEntityService;

    @Autowired
    private RpfWaybillItemEntityService rpfWaybillItemEntityService;

    @Override
    protected ViewService<RpfWaybill> getEntityService() {
        return rpfWaybillEntityService;
    }

    public void ship(Long id) {
        RpfWaybill waybill = this.get(id);
        if (!RpfWaybill.States.PENDING.equals(waybill.getState())) {
            throw new ServiceException(StandardErrors.CLIENT_ERROR.getStatus(), "error.rpfWaybill.has.shipped");
        }
        waybill.setState(RpfWaybill.States.DISPATCHED);
        this.save(waybill);
    }

    public PageResult<RpfWaybillItem> listItemsByPage(Long waybillId, PageSearch ps) {
        ps.getFilters().add(new ValueFilter("waybillId", ValueFilter.OP_EQ, waybillId));
        return rpfWaybillItemEntityService.search(ps);
    }

    public void receive(Long id, RpfWaybillReceiveRequest request) {
        RpfWaybill waybill = this.get(id);
        if (!RpfWaybill.States.DISPATCHED.equals(waybill.getState())) {
            throw new ServiceException(StandardErrors.CLIENT_ERROR.getStatus(), "error.csfWaybill.has.received");
        }
        waybill.setState(RpfWaybill.States.RECEIVED);
        waybill.setReceiptTime(request.getReceiptTime());
        waybill.setReceiptPlace(request.getReceiptPlace());
        this.save(waybill);
    }

    public RpfWaybill findByContractId(Long contractId) {
        return rpfWaybillEntityService.findByContractId(contractId);
    }

    public RpfWaybill findAvailableByContractId(Long contractId, Long trainingId) {
        return rpfWaybillEntityService.findAvailableByContractId(contractId, trainingId);
    }

    public RpfWaybill checkAndGetByContractId(Long trainingId, Long contractId) {
        NativeQueryBuilder builder = NativeQueryBuilderFactory.create()
                .putAlias("a", RpfWaybill.class)
                .selectForm("SELECT a.* FROM rpf_waybill a ")
                .where(" a.training_id = :trainingId")
                .where(" a.contract_id = :contractId")
                .where("NOT EXISTS (select 1 from rpf_loan rl where rl.waybill_id = a.id and rl.deleted = 0) ")
                .putVariable("trainingId", trainingId)
                .putVariable("contractId", contractId)
                .transform(RpfWaybill.class);
        List<RpfWaybill>  waybillList =  (List<RpfWaybill>) builder.build();
        if (waybillList.size() != 1) {
            throw new ServiceException(StandardErrors.EXTERNAL_ERROR.getStatus(), "error.rpfWaybill.not.available");
        }
        return waybillList.get(0);
    }

    public PageResult<RpfWaybill> loanSelection(PageSearch ps) {
        WhereModifier modifier = AuthorityFilterChain.getInstance().createNativeModifier("a", RpfWaybill.class, Rights.READ);
        ps.getFilters().add(new ValueFilter("state", ValueFilter.OP_EQ, RpfWaybill.States.RECEIVED));

        NativeQueryBuilder builder = NativeQueryBuilderFactory.create()
                .putAlias("a", RpfWaybill.class)
                .selectForm("SELECT a.* FROM rpf_waybill a ")
                .where("NOT EXISTS (select 1 from rpf_loan rl where rl.waybill_id = a.id and rl.deleted = 0) ")
                .where(ps)
                .where(modifier.getSql()).putVariables(modifier.getVariables())
                .transform(RpfWaybill.class);
        return builder.buildPage();
    }

    public PageResult<RpfWaybill> pledgeContractSelection(PageSearch ps) {
        WhereModifier modifier = AuthorityFilterChain.getInstance().createNativeModifier("a", RpfWaybill.class, Rights.READ);
        ps.getFilters().add(new ValueFilter("state", ValueFilter.OP_EQ, RpfWaybill.States.RECEIVED));

        NativeQueryBuilder builder = NativeQueryBuilderFactory.create()
                .putAlias("a", RpfWaybill.class)
                .selectForm("SELECT a.* FROM rpf_waybill a ")
                .where("NOT EXISTS (select 1 from rpf_pledge_contract rl where rl.waybill_id = a.id and rl.deleted = 0) ")
                .where(ps)
                .where(modifier.getSql()).putVariables(modifier.getVariables())
                .transform(RpfWaybill.class);
        return builder.buildPage();
    }

    public PageResult<Company> sellerSelection(Long trainingId, PageSearch ps) {
        ps.setFilters(new ArrayList<>());
        NativeQueryBuilder builder = NativeQueryBuilderFactory.create()
                .putAlias("a", Company.class)
                .selectForm("SELECT a.* FROM company a ")
                .where("EXISTS (select 1 from rpf_waybill rw where rw.training_id = :trainingId and rw.shipper = a.id and rw.state = 'RECEIVED' and rw.deleted = 0) ")
                .where(ps)
                .putVariable("trainingId", trainingId)
                .setAggregateWith(" group by a.id")
                .transform(Company.class);
        return builder.buildPage();


    }
}
